#include "CIOCPServerModel.h"
#include <process.h>
#include <mstcpip.h>

// 每一个处理器上产生多少个线程(为了最大限度的提升服务器性能，详见配套文档)
#define WORKER_THREADS_PER_PROCESSOR 2
// 同时投递的Accept请求的数量(这个要根据实际的情况灵活设置)
#define MAX_POST_ACCEPT              10
// 监听线程数量上限
#define MAX_THREAD_NUM               4

// 释放指针宏
#define SAFE_RELEASE(x)                 {if (x != NULL) {delete x;x=NULL;} }
// 释放句柄宏
#define RELEASE_HANDLE(x)               {if (x != NULL && x!=INVALID_HANDLE_VALUE){ CloseHandle(x); x = NULL;} }
// 释放Socket宏
#define RELEASE_SOCKET(x)               {if (x !=INVALID_SOCKET) { closesocket(x); x = INVALID_SOCKET;}}

#ifndef MAX
#define MAX(a,b)            (((a) > (b)) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b)            (((a) < (b)) ? (a) : (b))
#endif

unsigned int CIOCPServerModel::ms_uiConnectID = 1;

template <class Dst> Dst Reinterpret_CastT(const void* ptr)
{
	union
	{
		const void* src;
		Dst dst;
	} data;
	data.src = ptr;
	return data.dst;
}

CIOCPServerModel::CIOCPServerModel(LPCSTR lpAddress, int nPort, LPIOCPServerCallBack lpCb) :
	m_iocp(0),
	m_bServerStarted(FALSE),
	m_nThreads(0),
	m_hShutdownEvent(NULL),
	m_phWorkerThreads(NULL),
	m_IocpSrvCbPtr(lpCb),
	m_strIP(lpAddress),
	m_nPort(nPort),
	m_lpfnAcceptEx(NULL),
	m_pListenContext(NULL)
{
}

CIOCPServerModel::~CIOCPServerModel(void)
{
	// 确保资源彻底释放
	this->Stop();
}

// Test for send
/*static unsigned WINAPI SendThread(LPVOID lpParam)
{
	CIOCPServerModel* pIOCPModel = (CIOCPServerModel*)lpParam;
	int id = 0;
	char szBuf[20] = { 0 };
	while (1)
	{
		sprintf_s(szBuf, "send data %d", id++);
		pIOCPModel->SendDataByConnectID(0, szBuf, (ULONG)(strlen(szBuf) + 1));
		Sleep(10);
	}
	return 0;
} */

///////////////////////////////////////////////////////////////////
// 工作者线程：  为IOCP请求服务的工作者线程
// 也就是每当完成端口上出现了完成数据包，就将之取出来进行处理的线程
///////////////////////////////////////////////////////////////////
unsigned WINAPI CIOCPServerModel::_WorkerThread(LPVOID lpParam)
{
	CIOCPServerModel* pIOCPModel = (CIOCPServerModel*)lpParam;

	LPPER_IO_CONTEXT pIoContext = NULL;
	LPPER_SOCKET_CONTEXT pSocketContext = NULL;
	DWORD                dwBytesTransfered = 0;

	// 循环处理请求，知道接收到Shutdown信息为止
	while (WAIT_OBJECT_0 != WaitForSingleObject(pIOCPModel->m_hShutdownEvent, 0))
	{
		BOOL bRet = pIOCPModel->m_iocp.GetStatus((PULONG_PTR)&pSocketContext, &dwBytesTransfered,
			(LPOVERLAPPED*)&pIoContext, INFINITE);

		// 如果收到的是退出标志，则直接退出
		if (EXIT_POSTED == Reinterpret_CastT<DWORD>(pSocketContext))
		{
			printf("receive exit message, disconnected.\n");
			break;
		}

		// 判断是否出现了错误
		if (!bRet)
		{
			DWORD dwErr = GetLastError();
			// 显示一下提示信息
			if (!pIOCPModel->HandleError(pSocketContext, dwErr))
			{
				pIOCPModel->_RemoveContext(pSocketContext);
			}
			continue;
		}
		else
		{
			// 判断是否有客户端断开了
			if ((0 == dwBytesTransfered) && (RECV_POSTED == pIoContext->m_OpType || SEND_POSTED == pIoContext->m_OpType))
			{
				printf("client:%s:%d  connectID:%d disconnected.\n", inet_ntoa(pSocketContext->m_ClientAddr.sin_addr), ntohs(pSocketContext->m_ClientAddr.sin_port), pSocketContext->m_uiConnectID);

				if (pIOCPModel->m_IocpSrvCbPtr)
				{
					pIOCPModel->m_IocpSrvCbPtr->OnConnectionClosed(pSocketContext);
				}
				// 释放掉对应的资源
				pIOCPModel->_RemoveContext(pSocketContext);
				continue;
			}
			else
			{
				switch (pIoContext->m_OpType)
				{
				// Accept  
				case ACCEPT_POSTED:
				{
					// 这里用专门的_DoAccept函数进行处理连入请求
					pIOCPModel->_DoAccpet(pSocketContext, pIoContext);
				}
				break;
				// RECV
				case RECV_POSTED:
				{
					// 这里用专门的_DoRecv函数进行处理接收请求
					pIOCPModel->_DoRecv(pSocketContext, pIoContext, dwBytesTransfered);
				}
				break;
				// SEND
				case SEND_POSTED:
				{
					// WsaSend ok
					pIOCPModel->_DoSend(pSocketContext, pIoContext, dwBytesTransfered);
				}
				break;
				default:
					// 不应该执行到这里
					break;
				} //switch
			} //if
		} //if
	} //while
	return 0;
}

////////////////////////////////////////////////////////////////////
// 初始化WinSock 2.2
bool CIOCPServerModel::LoadSocketLib()
{
	WSADATA wsaData;
	int nResult;
	nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	// 错误(一般都不可能出现)
	if (NO_ERROR != nResult)
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]初始化WinSock 2.2失败！ init socket error!!!");
			m_IocpSrvCbPtr->OnIOCPCaughtError(NET_IOCP_WSAStartup, WSAGetLastError());
		}
		return false;
	}
	return true;
}

void CIOCPServerModel::UnloadSocketLib()
{
	WSACleanup();
}

//////////////////////////////////////////////////////////////////
//	启动服务器
BOOL CIOCPServerModel::Start()
{
	if (m_bServerStarted)
	{
		return TRUE;
	}
	// 建立系统退出的事件通知
	m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	if (false == LoadSocketLib())
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]加载Winsock 2.2失败，服务器端无法运行！");
		}
		return FALSE;
	}

	// 初始化IOCP
	if (false == _InitializeIOCP())
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]初始化IOCP失败, error:%d", GetLastError());
		}
		return FALSE;
	}

	// 初始化Socket
	if (false == _InitializeListenSocket())
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]Listen Socket初始化失败！");
		}
		this->_DeInitialize();
		return FALSE;
	}
	m_bServerStarted = TRUE;
	return TRUE;
}

////////////////////////////////////////////////////////////////////
//	开始发送系统退出消息，退出完成端口和线程资源
BOOL CIOCPServerModel::Stop()
{
	if (m_bServerStarted)
	{
		// 激活关闭消息通知
		SetEvent(m_hShutdownEvent);
		for (int i = 0; i < m_nThreads; i++)
		{
			// 通知所有的完成端口操作退出
			m_iocp.PostStatus(EXIT_POSTED);
		}

		// 等待所有的客户端资源退出
		WaitForMultipleObjects(m_nThreads, m_phWorkerThreads, TRUE, INFINITE);

		// 清除客户端列表信息
		this->_ClearContextList();

		// 释放其他资源
		this->_DeInitialize();
	}
	m_bServerStarted = FALSE;
	return TRUE;
}

////////////////////////////////
// 初始化完成端口
bool CIOCPServerModel::_InitializeIOCP()
{
	// 根据本机中的处理器数量，建立对应的线程数
	m_nThreads = WORKER_THREADS_PER_PROCESSOR * _GetNoOfProcessors();

	//没必要那么多线程
	m_nThreads = MIN(m_nThreads, MAX_THREAD_NUM);
	// 为工作者线程初始化句柄
	m_phWorkerThreads = new HANDLE[m_nThreads];

	// 根据计算出来的数量建立工作者线程
	for (int i = 0; i < m_nThreads; i++)
	{
		m_phWorkerThreads[i] = (HANDLE)::_beginthreadex(NULL, 0, _WorkerThread, this, 0, nullptr);
	}

	//TRACE(" 建立 _WorkerThread %d 个.\n", m_nThreads );
	return true;
}

/////////////////////////////////////////////////////////////////
// 初始化Socket
bool CIOCPServerModel::_InitializeListenSocket()
{
	// AcceptEx 和 GetAcceptExSockaddrs 的GUID，用于导出函数指针
	GUID GuidAcceptEx = WSAID_ACCEPTEX;
	GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;

	// 服务器地址信息，用于绑定Socket
	struct sockaddr_in ServerAddress;

	// 生成用于监听的Socket的信息
	m_pListenContext = new PER_SOCKET_CONTEXT;

	// 需要使用重叠IO，必须得使用WSASocket来建立Socket，才可以支持重叠IO操作
	m_pListenContext->m_Socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (INVALID_SOCKET == m_pListenContext->m_Socket)
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]初始化Socket失败!错误代码: %d.", WSAGetLastError());
			m_IocpSrvCbPtr->OnIOCPCaughtError(NET_IOCP_Sock, WSAGetLastError());
		}
		return false;
	}

	// 将Listen Socket绑定至完成端口中
	if (!m_iocp.AssociateSocket(m_pListenContext->m_Socket, (ULONG_PTR)m_pListenContext))
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]绑定 Listen Socket至完成端口失败！错误代码: %d", WSAGetLastError());
			m_IocpSrvCbPtr->OnIOCPCaughtError(NET_IOCP_CreateIoCompletionPort, WSAGetLastError());
		}
		RELEASE_SOCKET( m_pListenContext->m_Socket );
		return false;
	}
	else
	{
		//TRACE("Listen Socket绑定完成端口 完成.\n");
	}

	// 填充地址信息
	ZeroMemory((char *)&ServerAddress, sizeof(ServerAddress));
	ServerAddress.sin_family = AF_INET;
	// 这里可以绑定任何可用的IP地址，或者绑定一个指定的IP地址
	ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);
	//ServerAddress.sin_addr.s_addr = inet_addr(m_strIP.c_str());
	ServerAddress.sin_port = htons(m_nPort);

	// 绑定地址和端口
	if (SOCKET_ERROR == bind(m_pListenContext->m_Socket, (struct sockaddr *)&ServerAddress, sizeof(ServerAddress)))
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]bind()函数执行错误.！错误代码: %d", WSAGetLastError());
			m_IocpSrvCbPtr->OnIOCPCaughtError(NET_IOCP_Bind, WSAGetLastError());
		}
		return false;
	}
	else
	{
		//TRACE("bind() 完成.\n");
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"bind socket succ. tid:%d", GetCurrentThreadId());
		}
	}

	// 开始进行监听
	if (SOCKET_ERROR == listen(m_pListenContext->m_Socket, SOMAXCONN))
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]Listen()函数执行错误.！错误代码: %d", WSAGetLastError());
			m_IocpSrvCbPtr->OnIOCPCaughtError(NET_IOCP_Listen, WSAGetLastError());
		}
		return false;
	}
	else
	{
		//TRACE("Listen() 完成.\n");
	}

	// 使用AcceptEx函数，因为这个是属于WinSock2规范之外的微软另外提供的扩展函数
	// 所以需要额外获取一下函数的指针，
	// 获取AcceptEx函数指针
	DWORD dwBytes = 0;
	if(SOCKET_ERROR == WSAIoctl(
		m_pListenContext->m_Socket,
		SIO_GET_EXTENSION_FUNCTION_POINTER,
		&GuidAcceptEx,
		sizeof(GuidAcceptEx),
		&m_lpfnAcceptEx,
		sizeof(m_lpfnAcceptEx),
		&dwBytes,
		NULL,
		NULL))
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]WSAIoctl 未能获取AcceptEx函数指针。错误代码 : %d", WSAGetLastError());
			m_IocpSrvCbPtr->OnIOCPCaughtError(NET_IOCP_WSAIOCTL, WSAGetLastError());
		}
		this->_DeInitialize();
		return false;
	}

	// 获取GetAcceptExSockAddrs函数指针，也是同理
	if(SOCKET_ERROR == WSAIoctl(
		m_pListenContext->m_Socket,
		SIO_GET_EXTENSION_FUNCTION_POINTER,
		&GuidGetAcceptExSockAddrs,
		sizeof(GuidGetAcceptExSockAddrs),
		&m_lpfnGetAcceptExSockAddrs,
		sizeof(m_lpfnGetAcceptExSockAddrs),
		&dwBytes,
		NULL,
		NULL))
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]WSAIoctl 未能获取GuidGetAcceptExSockAddrs函数指针。错误代码 : %d", WSAGetLastError());
			m_IocpSrvCbPtr->OnIOCPCaughtError(NET_IOCP_WSAIOCTL, WSAGetLastError());
		}
		this->_DeInitialize();
		return false;
	}

	// 为AcceptEx 准备参数，然后投递AcceptEx I/O请求
	for (int i=0; i < MAX_POST_ACCEPT; i++)
	{
		// 新建一个IO_CONTEXT
		LPPER_IO_CONTEXT pAcceptIoContext = m_pListenContext->GetNewIoContext();
		if (false == this->_PostAccept(pAcceptIoContext))
		{
			if (m_IocpSrvCbPtr)
			{
				m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP] post accept code failed。错误代码 : %d", WSAGetLastError());
				m_IocpSrvCbPtr->OnIOCPCaughtError(NET_IOCP_WSAIOCTL, WSAGetLastError());
			}
			m_pListenContext->RemoveObj(pAcceptIoContext);
		}
	}

	if (m_IocpSrvCbPtr)
	{
		m_IocpSrvCbPtr->OnIOCPInitializeFinished();
	}
	return true;
}

////////////////////////////////////////////////////////////
//	最后释放掉所有资源
void CIOCPServerModel::_DeInitialize()
{
	//SetIOCPServerCallBackPtr(NULL);

	// 关闭系统退出事件句柄
	RELEASE_HANDLE(m_hShutdownEvent);

	// 释放工作者线程句柄指针
	for (int i=0; i < m_nThreads; i++)
	{
		RELEASE_HANDLE(m_phWorkerThreads[i]);
	}

	SAFE_RELEASE(m_phWorkerThreads);

	m_nThreads = 0;

	RemoveAll();

	// 关闭监听Socket
	SAFE_RELEASE(m_pListenContext);
}

//====================================================================================
//
//				    投递完成端口请求
//
//====================================================================================

//////////////////////////////////////////////////////////////////
// 投递Accept请求
bool CIOCPServerModel::_PostAccept(LPPER_IO_CONTEXT pAcceptIoContext)
{
	assert(INVALID_SOCKET != m_pListenContext->m_Socket);

	// 准备参数
	DWORD dwBytes = 0;
	pAcceptIoContext->m_OpType = ACCEPT_POSTED;

	// 为以后新连入的客户端先准备好Socket( 这个是与传统accept最大的区别 )
	pAcceptIoContext->m_sockAccept  = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_HOPOPTS/*IPPROTO_TCP*/, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (INVALID_SOCKET == pAcceptIoContext->m_sockAccept)
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]创建用于Accept的Socket失败！错误代码: %d", WSAGetLastError());
		}
		return false;
	}

	// 投递AcceptEx   将接收缓冲置为0,令AcceptEx直接返回,防止拒绝服务攻击,缺点是不能获取对方的ip了,优点是第一条消息不会附带在accept消息上
	if(FALSE == m_lpfnAcceptEx(m_pListenContext->m_Socket, pAcceptIoContext->m_sockAccept, pAcceptIoContext->m_wsaBuf.buf, 0 /*pAcceptIoContext->m_wsaBuf.len - ((sizeof(SOCKADDR_IN) + 16) * 2)*/,
								sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, &dwBytes, pAcceptIoContext))
	{
		if(WSA_IO_PENDING != WSAGetLastError())
		{
			if (m_IocpSrvCbPtr)
			{
				m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]投递 AcceptEx 请求失败，错误代码: %d", WSAGetLastError());
			}
			return false;
		}
	}
	return true;
}

////////////////////////////////////////////////////////////
// 在有客户端连入的时候，进行处理
// 原来的Context还是要在上面继续投递下一个Accept请求
bool CIOCPServerModel::_DoAccpet(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT pIoContext)
{
	SOCKADDR_IN* ClientAddr = NULL;
	SOCKADDR_IN* LocalAddr = NULL;
	int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);

	///////////////////////////////////////////////////////////////////////////
	// 1. 首先取得连入客户端的地址信息
	// 这个 m_lpfnGetAcceptExSockAddrs 不得了
	// 不但可以取得客户端和本地端的地址信息，还能顺便取出客户端发来的第一组数据
	this->m_lpfnGetAcceptExSockAddrs(pIoContext->m_wsaBuf.buf, pIoContext->m_wsaBuf.len - ((sizeof(SOCKADDR_IN)+16)*2),
		sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen);

	//////////////////////////////////////////////////////////////////////////////////////////////////////
	// 2. 这里需要注意，这里传入的这个是ListenSocket上的Context，这个Context我们还需要用于监听下一个连接
	// 所以我还得要将ListenSocket上的Context复制出来一份为新连入的Socket新建一个SocketContext
	PER_SOCKET_CONTEXT* pNewSocketContext = new PER_SOCKET_CONTEXT;
	pNewSocketContext->m_Socket = pIoContext->m_sockAccept;
	pNewSocketContext->m_uiConnectID = ms_uiConnectID ++;
	memcpy(&(pNewSocketContext->m_ClientAddr), ClientAddr, sizeof(SOCKADDR_IN));

	printf_s("client:%s:%d connectID:%d accept message:%s\n", inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port),
		pNewSocketContext->m_uiConnectID, pIoContext->m_wsaBuf.buf);

	// 将句柄(Socket)绑定到完成端口中
	if (!m_iocp.AssociateSocket(pNewSocketContext->m_Socket, (ULONG_PTR)pNewSocketContext))
	{
		SAFE_RELEASE( pNewSocketContext );
		return false;
	}

	// 并设置tcp_keepalive
	tcp_keepalive alive_in;
	tcp_keepalive alive_out;
	alive_in.onoff = TRUE;
	alive_in.keepalivetime = 1000 * 60;  // 60s  多长时间（ ms ）没有数据就开始 send 心跳包
	alive_in.keepaliveinterval = 1000 * 10; //10s  每隔多长时间（ ms ） send 一个心跳包
	unsigned long ulBytesReturn = 0;
	if (SOCKET_ERROR == WSAIoctl(pNewSocketContext->m_Socket, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in), &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL))
	{
		//_TRACE(L"WSAIoctl failed: %d/n", WSAGetLastError());
	}

	if (m_IocpSrvCbPtr)
	{
		m_IocpSrvCbPtr->OnConnectionEstablished(pNewSocketContext);
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////
	// 3. 继续，建立其下的IoContext，用于在这个Socket上投递第一个Recv数据请求
	LPPER_IO_CONTEXT pNewIoContext = pNewSocketContext->GetNewIoContext();
	pNewIoContext->m_sockAccept    = pNewSocketContext->m_Socket;
	pNewIoContext->m_uiConnectID   = pNewSocketContext->m_uiConnectID;
	// 如果Buffer需要保留，就自己拷贝一份出来
	//memcpy( pNewIoContext->m_szBuffer,pIoContext->m_szBuffer,MAX_BUFFER_LEN );

	// 绑定完毕之后，就可以开始在这个Socket上投递完成请求了
	if (false==this->_PostRecv( pNewIoContext))
	{
		pNewSocketContext->RemoveObj(pNewIoContext);
		return false;
	}

	/////////////////////////////////////////////////////////////////////////////////////////////////
	// 4. 如果投递成功，那么就把这个有效的客户端信息，加入到ContextList中去(需要统一管理，方便释放资源)
	this->_AddToContextList(pNewSocketContext);

	////////////////////////////////////////////////////////////////////////////////////////////////
	// 5. 使用完毕之后，把Listen Socket的那个IoContext重置，然后准备投递新的AcceptEx
	pIoContext->ResetBuffer();
	return this->_PostAccept(pIoContext);
}

////////////////////////////////////////////////////////////////////
// 投递接收数据请求
bool CIOCPServerModel::_PostRecv(LPPER_IO_CONTEXT pIoContext)
{
	// 初始化变量
	DWORD dwFlags = 0;
	DWORD dwBytes = 0;

	//pIoContext->ResetBuffer();
	pIoContext->m_OpType = RECV_POSTED;

	// 初始化完成后，，投递WSARecv请求
	int nBytesRecv = WSARecv( pIoContext->m_sockAccept, &pIoContext->m_wsaBuf, 1, &dwBytes, &dwFlags, pIoContext, NULL );

	// 如果返回值错误，并且错误的代码并非是Pending的话，那就说明这个重叠请求失败了
	if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]投递第一个WSARecv失败！错误码: %d", WSAGetLastError());
		}
		return false;
	}
	return true;
}

BOOL CIOCPServerModel::_PostSend(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT ioContext, LPWSABUF lpWSABuf)
{
	ioContext->m_OpType = SEND_POSTED;
	DWORD dwBytes = 0, dwFlags = 0;

	if (pSocketContext->m_lPendingNum > 1000)
	{
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnOutPutMsg(L"[IOCP]WSASend WSA_IO_PENDING num too much and will not send, io pending num:%d",
				pSocketContext->m_lPendingNum);
		}
		return FALSE;
	}

	if (::WSASend(pSocketContext->m_Socket, lpWSABuf, 1,
		&dwBytes, dwFlags, (OVERLAPPED*)ioContext, NULL) != NO_ERROR)
	{
		DWORD dwWSAError = WSAGetLastError();
		if (dwWSAError == WSA_IO_PENDING)
		{
			//  WSA_IO_PENDING的时候，表示此时TCP/IP层缓冲区已满，暂时没有剩余的空间将程序缓冲区的数据拷贝出来，
			// 这时系统将锁定用户的程序缓冲区，按照书上说的WSASend指定的缓冲区将会被锁定到系统的非分页内存中。
			// 直到TCP/IP层缓冲区有空余的地方来接受拷贝我们的程序缓冲区数据才拷贝走，并将给IOCP一个完成消息。
			InterlockedIncrement(&pSocketContext->m_lPendingNum);
			printf("wsasend ok, wsaerror is WSA_IO_PENDING, pending num:%d\n", pSocketContext->m_lPendingNum);
			return TRUE;
		}
		else
		{
			printf("wsasend failed, wsaerror:%d  last error:%d\n", dwWSAError, GetLastError());
			pSocketContext->RemoveObj(ioContext);
			return FALSE;
		}
	}
	return TRUE;
}

/////////////////////////////////////////////////////////////////
// 在有接收的数据到达的时候，进行处理
bool CIOCPServerModel::_DoRecv(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT pIoContext, UINT uiBytesTrans)
{
	// 先把上一次的数据显示出现，然后就重置状态，发出下一个Recv请求
	SOCKADDR_IN* ClientAddr = &pSocketContext->m_ClientAddr;
	printf("Recive  %s:%d connectID:%d Message: %s, bytes:%d\n", inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port),
		pIoContext->m_uiConnectID, pIoContext->m_wsaBuf.buf, uiBytesTrans);
	
	if (pIoContext->m_uiBytesRecved == 0)
	{
		pIoContext->m_uiBytesRecved = uiBytesTrans;
		//PerIOData->BytesSend = 0;
	}
	else
	{
		pIoContext->m_uiBytesRecved += uiBytesTrans;
	}
	//pIoContext->m_uiBytesRecved += uiBytesTrans;
	if (pIoContext->m_wsaBuf.buf)
	{
		pSocketContext->m_llRecvTotalSize += (pIoContext->m_uiBytesRecved); // uiBytesTrans ?
		pSocketContext->m_uiSeq++;
	}
	//pIoContext->m_uiBytesRecved = 0;

	if (m_IocpSrvCbPtr)
	{
		m_IocpSrvCbPtr->OnRecvCompleted(pSocketContext, pIoContext, uiBytesTrans);
	}

	// 然后开始投递下一个WSARecv请求
	pIoContext->ResetBuffer();
	return _PostRecv(pIoContext);
}

/////////////////////////////////////////////////////////////////
// 在有发送的数据到达的时候，进行处理
bool CIOCPServerModel::_DoSend(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT pIoContext, UINT uiBytesTrans)
{
	SOCKADDR_IN* ClientAddr = &pSocketContext->m_ClientAddr;
	pIoContext->m_uiConnectID = pSocketContext->m_uiConnectID;
	printf("WSASend ok  %s:%d connectID:%d Pending:%d Message: %hs, size:%d\n", inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port),
		pIoContext->m_uiConnectID, pSocketContext->m_lPendingNum, pIoContext->m_wsaBuf.buf, uiBytesTrans);

	if (m_IocpSrvCbPtr)
	{
		m_IocpSrvCbPtr->OnSendCompleted(pSocketContext, pIoContext, uiBytesTrans);
	}

	if (!pSocketContext->AddToContextPool(pIoContext))
	{
		pSocketContext->RemoveObj(pIoContext);
	}

	if (pSocketContext->m_lPendingNum > 0)
	{
		InterlockedDecrement(&pSocketContext->m_lPendingNum);
	}
	return true;
}

//====================================================================================
//
//				    ContextList 相关操作
//
//====================================================================================

//////////////////////////////////////////////////////////////
// 将客户端的相关信息存储到数组中
void CIOCPServerModel::_AddToContextList(LPPER_SOCKET_CONTEXT pHandleData)
{
	AddObj(pHandleData);
}

////////////////////////////////////////////////////////////////
//	移除某个特定的Context
void CIOCPServerModel::_RemoveContext(LPPER_SOCKET_CONTEXT pSocketContext)
{
	RemoveObj(pSocketContext);
}

////////////////////////////////////////////////////////////////
// 清空客户端信息
void CIOCPServerModel::_ClearContextList()
{
	RemoveAll();
}

//====================================================================================
//
//				       其他辅助函数定义
//
//====================================================================================

////////////////////////////////////////////////////////////////////
// 获得本机的IP地址
std::string CIOCPServerModel::GetLocalIP()
{
	// 获得本机主机名
	char hostname[MAX_PATH] = {0};
	gethostname(hostname,MAX_PATH);
	struct hostent FAR* lpHostEnt = gethostbyname(hostname);
	if(lpHostEnt == NULL)
	{
		return DEFAULT_IP;
	}

	// 取得IP地址列表中的第一个为返回的IP(因为一台主机可能会绑定多个IP)
	LPSTR lpAddr = lpHostEnt->h_addr_list[0];

	// 将IP地址转化成字符串形式
	struct in_addr inAddr;
	memmove(&inAddr,lpAddr,4);
	 std::string strIP = inet_ntoa(inAddr);
	return strIP;
}

void CIOCPServerModel::SetIOCPServerCallBackPtr(LPIOCPServerCallBack pIocpSrvCb)
{
	m_IocpSrvCbPtr = pIocpSrvCb;
}

LPIOCPServerCallBack CIOCPServerModel::GetIOCPServerCallBackPtr()
{
	return m_IocpSrvCbPtr;
}

// 获取设置的ip
LPCSTR CIOCPServerModel::GetListenIP()
{
	return m_strIP.c_str();
}

// 获取设置的端口
INT CIOCPServerModel::GetPort()
{
	return m_nPort;
}

void CIOCPServerModel::SetListenIP(LPCSTR lpIPAddr)
{
	m_strIP = lpIPAddr;
}

void CIOCPServerModel::SetPort(const INT& nPort)
{
	m_nPort = nPort;
}

BOOL CIOCPServerModel::DisconnectByConnectID(UINT uiConID)
{
	for (int id = 0; id < GetCount(); id++)
	{
		LPPER_SOCKET_CONTEXT lpContext = GetObj(id);
		if (lpContext && lpContext->m_uiConnectID == uiConID)
		{
			::closesocket(lpContext->m_Socket);
			_RemoveContext(lpContext);
			return TRUE;
		}
	}
	return FALSE;
}

BOOL CIOCPServerModel::SendDataByConnectID(UINT uiConID, LPCSTR lpData, ULONG ulDataSize)
{
	LPPER_SOCKET_CONTEXT lpCon = NULL;
	for (INT id = 0; id < GetCount(); id++)
	{
		LPPER_SOCKET_CONTEXT lpContext = GetObj(id);
		if (lpContext && lpContext->m_uiConnectID == uiConID)
		{
			lpCon = lpContext;
			break;
		}
	}

	if (!lpCon)
	{
		return FALSE;
	}
	LPPER_IO_CONTEXT pIoContext = lpCon->GetNewIoContext();

	WSABUF wbuf;
	wbuf.len = ulDataSize;
	wbuf.buf = (char*)lpData;
	return _PostSend(lpCon, pIoContext, &wbuf);
}

void CIOCPServerModel::PreVecObjRemove(const LPPER_SOCKET_CONTEXT& obj) {
	delete obj;
}

///////////////////////////////////////////////////////////////////
// 获得本机中处理器的数量
int CIOCPServerModel::_GetNoOfProcessors()
{
	SYSTEM_INFO si;
	GetSystemInfo(&si);
	return si.dwNumberOfProcessors;
}

/////////////////////////////////////////////////////////////////////
// 判断客户端Socket是否已经断开，否则在一个无效的Socket上投递WSARecv操作会出现异常
// 使用的方法是尝试向这个socket发送数据，判断这个socket调用的返回值
// 因为如果客户端网络异常断开(例如客户端崩溃或者拔掉网线等)的时候，服务器端是无法收到客户端断开的通知的
bool CIOCPServerModel::_IsSocketAlive(SOCKET s)
{
	int nByteSent = send(s, "", 0, 0);
	if (-1 == nByteSent) return false;
	return true;
}

///////////////////////////////////////////////////////////////////
// 显示并处理完成端口上的错误
BOOL CIOCPServerModel::HandleError(PER_SOCKET_CONTEXT *pContext,const DWORD& dwErr)
{
	BOOL bDisConnected = TRUE;
	std::wstring strError;
	
	// 如果是超时了，就再继续等吧
	if (WAIT_TIMEOUT == dwErr)
	{
		// 确认客户端是否还活着...
		if (!_IsSocketAlive(pContext->m_Socket))
		{
			strError = L"[IOCP]检测到客户端异常退出！socket is not alive.ConnectID:%d.";
		}
		else
		{
			bDisConnected = FALSE;
			strError = L"[IOCP]网络操作超时！重试中...,ConnectID:%d.";
		}
	}
	else if (ERROR_NETNAME_DELETED == dwErr)
	{
		strError = L"[IOCP]检测到客户端异常退出！socket error is ERROR_NETNAME_DELETED. ConnectID:%d.";
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnConnectionError(pContext, dwErr);
		}
	}
	else
	{
		strError = L"[IOCP]检测完成端口操作出现错误.ConnectID:%d.";
		if (m_IocpSrvCbPtr)
		{
			m_IocpSrvCbPtr->OnConnectionError(pContext, dwErr);
		}
	}

	if (m_IocpSrvCbPtr)
	{
		if (bDisConnected)
		{
			m_IocpSrvCbPtr->OnConnectionClosed(pContext);
		}
		if (!strError.empty())
		{
			m_IocpSrvCbPtr->OnOutPutMsg(strError, pContext ? pContext->m_uiConnectID : 0);
		}
	}
	return (bDisConnected == FALSE);
}